/*
 * Thread Pool Demo
 * Spring 2013 TCSS 558
 * Daniel M. Zimmerman
 */

package threadpools;

import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * Picks a number (specified on the command line) of random pairs of
 * long integers (x, y) and computes x^y for each, using a thread
 * pool with a maximum number of threads also specified on the
 * command line.
 * 
 * @author Daniel M. Zimmerman
 * @version Spring 2013
 */
public final class ExponentiateMain
{
  /**
   * The default maximum number of threads.
   */
  public static final int DEFAULT_NUM_THREADS = 5;
  
  /**
   * The default number of (x, y) pairs to exponentiate.
   */
  public static final int DEFAULT_NUM_PAIRS = 50;

  /**
   * The maximum exponent.
   */
  public static final int MAX_EXPONENT = 200; 
  
  // Constructor
  
  /**
   * Empty constructor to prevent instantiation.
   */
  private ExponentiateMain()
  {
    
  }
  
  // Main Method
  
  /**
   * Creates a specified number of threads (or the default, if none
   * is specified) and runs them.
   * 
   * @param the_args Command line arguments; argument 0, if present and
   * an integer, is the number of threads to create.
   */
  public static void main(final String... the_args)
  {
    final Random random = new Random();
    final SimpleDateFormat date = 
        new SimpleDateFormat("yyyy-MM-dd' 'HH:mm:ss.SSS", Locale.US);
    int num_threads = DEFAULT_NUM_THREADS;
    int num_pairs = DEFAULT_NUM_PAIRS;
    
    if (the_args.length > 0)
    {
      try
      {
        num_pairs = Integer.parseInt(the_args[0]);
      }
      catch (final NumberFormatException e)
      {
        num_pairs = DEFAULT_NUM_PAIRS;
      }
    }
    
    if (the_args.length > 1)
    {
      try
      {
        num_threads = Integer.parseInt(the_args[1]);
      }
      catch (final NumberFormatException e)
      {
        num_threads = DEFAULT_NUM_THREADS;
      }
    }
    
    // create a thread pool, with unbounded queue, that
    // has a maximum of num_threads active threads
    
    final ExecutorService thread_pool =
      Executors.newFixedThreadPool(num_threads);
    
    final int[] x = new int[num_pairs];
    final int[] y = new int[num_pairs];
    
    // compute the operands first, so we can enqueue everything
    // at once and really see how thread pools work
    
    System.out.println(date.format(System.currentTimeMillis()) +
                       " Generating operands...");
    
    for (int i = 0; i < num_pairs; i++)
    {
      x[i] = -1;
      y[i] = -1;
      
      while (x[i] < 0)
      {
        x[i] = random.nextInt();
      }
      while (y[i] < 0 || y[i] > MAX_EXPONENT)
      {
        y[i] = random.nextInt();
      }
    }
    
    System.out.println(date.format(System.currentTimeMillis()) +
        " Operands generated.");

    // now enqueue the runnables
    
    for (int i = 0; i < num_pairs; i++)
    {
      System.out.println(date.format(System.currentTimeMillis()) + 
                         " Enqueuing thread to compute " + x[i] + 
                         "^" + y[i]);
      thread_pool.submit(new Exponentiator(x[i], y[i]));
    }
    
    System.out.println(date.format(System.currentTimeMillis()) +
                       " Enqueueing complete");
    
    thread_pool.shutdown();
    while (!thread_pool.isTerminated())
    {
      try
      {
        // highly unlikely that it will take more than 1 hour
        // to do this computation
        thread_pool.awaitTermination(1, TimeUnit.HOURS);
      }
      catch (final InterruptedException e)
      {
        System.err.println("unexpected interrupt awaiting termination");
      }
    }
  }
}
